#include "netclient.h"

#include <QNetworkReply>
#include <QUuid>

#include "../model/data.h"
#include "../model/datacenter.h"

using namespace model;


namespace network
{
NetClient::NetClient(model::DataCenter *dataCenter)
    : dataCenter(dataCenter)
{
    // 不应该在这初始化
    // initWebsocket();
}

void NetClient::ping()
{
    QNetworkRequest httpReq;
    httpReq.setUrl(QUrl(HTTP_URL + "/ping"));

    QNetworkReply* httpResp = httpClient.get(httpReq);
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // 响应回来了
        if (httpResp->error() != QNetworkReply::NoError)
        {
            // 请求失败
            LOG("HTTP 请求失败!") << httpResp->errorString();
            httpResp->deleteLater();
            return;
        }
        // 获取到响应的 body
        QByteArray body = httpResp->readAll();
        LOG("响应内容: ") << body;
        httpResp->deleteLater();
    });
}

///////////////////////////////////////////////////
/// websocket 相关操作
//////////////////////////////////////////////////

void NetClient::initWebsocket()
{
    // 1. 准备好所有需要的信号槽
    connect(&websocketClient, &QWebSocket::connected, this, [=](){
        LOG("websocket 连接成功!");
        // 发送身份认证
        sendAuth();
    });

    connect(&websocketClient, &QWebSocket::disconnected, this, [=](){
        LOG("websocket 连接失败!");
    });

    connect(&websocketClient, &QWebSocket::errorOccurred, this, [=](QAbstractSocket::SocketError error){
        LOG("websocket 连接出错!") << error;
    });

    connect(&websocketClient, &QWebSocket::textMessageReceived, this, [=](const QString& message){
        LOG("websocket 收到文本消息!") << message;
    });

    connect(&websocketClient, &QWebSocket::binaryMessageReceived, this, [=](const QByteArray& byteArray){
        LOG("websocket 收到二进制消息!") << byteArray.length();
        bite_im::NotifyMessage notifyMessage;
        notifyMessage.deserialize(&serializer, byteArray);
        handleWsResponse(notifyMessage);
    });

    // 2. 和服务器真正建立连接
    websocketClient.open(WEBSOCKET_URL);
}

void NetClient::sendAuth()
{
    bite_im::ClientAuthenticationReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(dataCenter->getloginSessionId());
    QByteArray body = req.serialize(&serializer);
    websocketClient.sendBinaryMessage(body);
    LOG("[WS]身份认证 requestId = ") << req.requestId() << ", loginSessionId = " << req.sessionId();
}

void NetClient::handleWsResponse(const bite_im::NotifyMessage &notifyMessage)
{
    if(notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::CHAT_MESSAGE_NOTIFY)
    {
        // 收到消息
        // 1. 把 pb 中的 MessageInfo 转成客户端自己的 Message
        Message message;
        message.load(notifyMessage.newMessageInfo().messageInfo());
        // 2. 针对自己的 message 做进一步的处理
        handleWsMessage(message);
    }
    else if(notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::CHAT_SESSION_CREATE_NOTIFY)
    {
        // 创建新的会话通知
        ChatSessionInfo chatSessionInfo;
        chatSessionInfo.load(notifyMessage.newChatSessionInfo().chatSessionInfo());
        handleWsSessionCreate(chatSessionInfo);
    }
    else if(notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::FRIEND_ADD_APPLY_NOTIFY)
    {
        // 添加好友申请通知
        UserInfo userInfo;
        userInfo.load(notifyMessage.friendAddApply().userInfo());
        handleWsAddFriendApply(userInfo);
    }
    else if(notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::FRIEND_ADD_PROCESS_NOTIFY)
    {
        // 添加好友申请的处理结果通知
        UserInfo userInfo;
        userInfo.load(notifyMessage.friendProcessResult().userInfo());
        bool agree = notifyMessage.friendProcessResult().agree();
        handleWsAddFriendProcess(userInfo, agree);
    }
    else if(notifyMessage.notifyType() == bite_im::NotifyTypeGadget::NotifyType::FRIEND_REMOVE_NOTIFY)
    {
        // 删除好友通知
        const QString& userId = notifyMessage.friendRemove().userId();
        handleWsRemoveFriend(userId);
    }
}

void NetClient::handleWsMessage(const Message &message)
{
    // 两种情况
    QList<Message>* messageList = dataCenter->getRecentMessageList(message.chatSessionId);
    if (messageList == nullptr)
    {
        // 1. 如果当前这个消息所属的会话 里面的消息列表 没有在本地加载 通过网络加载
        connect(dataCenter, &DataCenter::getRecentMessageListDoneNoUI, this,
                &NetClient::receiveMessage, Qt::UniqueConnection);
        dataCenter->getRecentMessageListAsync(message.chatSessionId, false);
    }
    else
    {
        // 2. 如果当前这个消息所属的会话 里面的消息列表 已经在本地加载 直接尾插即可
        messageList->push_back(message);
        this->receiveMessage(message.chatSessionId);
    }
}

void NetClient::handleWsRemoveFriend(const QString &userId)
{
    // 1. 删除数据 DataCenter 好友列表的数据
    dataCenter->removeFriend(userId);
    // 2. 通知界面变化 更新 好友列表 / 会话列表
    emit dataCenter->deleteFriendDone();
}

void NetClient::handleWsAddFriendApply(const UserInfo &userInfo)
{
    // 1. DataCenter 中有一个 好友申请列表 需要把这个数据添加到好友申请列表中
    QList<UserInfo>* applyList = dataCenter->getApplyList();
    if (applyList == nullptr)
    {
        LOG("客户端没有加载到好友申请列表!");
        return;
    }
    // 指定新会话
    applyList->push_front(userInfo);

    // 2. 界面更新
    emit dataCenter->receiveFriendApplyDone();
}

void NetClient::handleWsAddFriendProcess(const UserInfo &userInfo, bool agree)
{
    if (agree)
    {
        // 同意好友申请
        QList<UserInfo>* friendList = dataCenter->getFriendList();
        if (friendList == nullptr)
        {
            LOG("客户端没有加载好友列表");
            return;
        }
        friendList->push_front(userInfo);

        // 更新界面
        emit dataCenter->receiveFriendProcessDone(userInfo.nickname, agree);
    }
    else
    {
        //  拒绝好友申请
        emit dataCenter->receiveFriendProcessDone(userInfo.nickname, agree);
    }
}

void NetClient::handleWsSessionCreate(const ChatSessionInfo &chatSessionInfo)
{
    // chatSessionInfo 添加会话列表
    QList<ChatSessionInfo>* chatSessionList = dataCenter->getChatSessionList();
    if (chatSessionList == nullptr)
    {
        LOG("客户端没有加载会话列表");
        return ;
    }
    // 头部添加
    chatSessionList->push_front(chatSessionInfo);
    // 发送一个信号 通知界面更新
    emit dataCenter->receiveSessionCreateDone();
}


///////////////////////////////////////////////////
/// http 相关操作
///////////////////////////////////////////////////


QString NetClient::makeRequestId()
{
    // 每个id 不重复
    // UUID
    // return QUuid::createUuid().toString();
    return "R" + QUuid::createUuid().toString().sliced(25, 12);
}

// 封装发送 HTTP 请求操作
QNetworkReply *NetClient::sendHttpRequest(const QString &apiPath, const QByteArray &body)
{
    QNetworkRequest httpReq;
    httpReq.setUrl(QUrl(HTTP_URL + apiPath));
    httpReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-protobuf");

    QNetworkReply* httpResp = httpClient.post(httpReq, body);
    return httpResp;
}

void NetClient::getMyself(const QString &loginSessionId)
{
    // 1. 构造出 HTTP 请求出 body 部分
    bite_im::GetUserInfoReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG("[获取个人信息] 发送请求 requestId = ") << req.requestId() << ", loginSessionId = " << loginSessionId;

    // 2. 构造出 HTTP 请求，并发出去
    QNetworkReply* httpResp = sendHttpRequest("/service/user/get_user_info", body);

    // 3. 通过信号槽 获取当前的响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 处理响应对象
        bool ok = false;
        QString reason;
        auto resp = handleHttpResponse<bite_im::GetUserInfoRsp>(httpResp, &ok, &reason);

        // b> 判定响应
        if (!ok)
        {
            LOG("[获取个人信息] 出错! requestId = ") << req.requestId() << "reason = " << reason;
            return;
        }

        // c> 把响应的数据，保存到 DataCenter 中
        dataCenter->resetMyself(resp);

        // d> 通知调用逻辑 响应已经处理完了 通过信号槽通知
        emit dataCenter->getMyselfDone();

        // d> 打印日志
        LOG("[获取个人信息] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::getFriendList(const QString& loginSessionId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::GetFriendListReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG("[获取好友列表] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << loginSessionId;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/get_friend_list", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto friendListResp = this->handleHttpResponse<bite_im::GetFriendListRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[获取好友列表] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetFriendList(friendListResp);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->getFriendListDone();

        // e> 打印日志
        LOG("[获取好友列表] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::getChatSessionList(const QString &loginSessionId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::GetChatSessionListReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG("[获取会话列表] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << loginSessionId;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/get_chat_session_list", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto chatSessionListResp = this->handleHttpResponse<bite_im::GetChatSessionListRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[获取会话列表] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetChatSessionList(chatSessionListResp);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->getChatSessionListDone();

        // e> 打印日志
        LOG("[获取会话列表] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::getApplyList(const QString &loginSessionId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::GetPendingFriendEventListReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG("[获取好友申请列表] 发送请求 requestId = ") << req.requestId()
                                                    << ", loginSessionId = " << loginSessionId;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/get_pending_friend_events", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto applyListResp = this->handleHttpResponse<bite_im::GetPendingFriendEventListRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[获取好友申请列表] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetApplyList(applyListResp);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->getApplyListDone();

        // e> 打印日志
        LOG("[获好友申请列表] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::getRecentMessageList(const QString &loginSessionId, const QString &chatSessionId,
                                    bool updateUI)
{
    // 1. 通过 protobuf 构造 body
    bite_im::GetRecentMsgReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);
    req.setMsgCount(50);
    QByteArray body = req.serialize(&serializer);
    LOG("[获取最近消息] 发送请求 requestId = ") << req.requestId()
                                                    << ", chatSessionId = " << chatSessionId;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/message_storage/get_recent", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto recentMegResp = this->handleHttpResponse<bite_im::GetRecentMsgRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[获取最近消息] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetRecentMessageList(chatSessionId, recentMegResp);

        // d> 发送信号 通知界面 操作完成
        if (updateUI)
            emit dataCenter->getRecentMessageListDone(chatSessionId);
        else
            emit dataCenter->getRecentMessageListDoneNoUI(chatSessionId);

        LOG("[获取最近消息] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::sendMessage(const QString &loginSessionId, const QString &chatSessionId,
                            MessageType messageType, const QByteArray &content, const QString& extraInfo)
{
    // 1. 通过 protobuf 构造 body
    bite_im::NewMessageReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);

    // 构造 MessageContent
    bite_im::MessageContent messageContent;
    if (messageType == MessageType::TEXT_TYPE)
    {
        messageContent.setMessageType(bite_im::MessageTypeGadget::MessageType::STRING);

        bite_im::StringMessageInfo stringMessageInfo;
        stringMessageInfo.setContent(content);
        messageContent.setStringMessage(stringMessageInfo);
    }
    else if (messageType == MessageType::IMAGE_TYPE)
    {
        messageContent.setMessageType(bite_im::MessageTypeGadget::MessageType::IMAGE);

        bite_im::ImageMessageInfo imageMessageInfo;
        imageMessageInfo.setFileId("");
        imageMessageInfo.setImageContent(content);
        messageContent.setImageMessage(imageMessageInfo);
    }
    else if (messageType == MessageType::FILE_TYPE)
    {
        messageContent.setMessageType(bite_im::MessageTypeGadget::MessageType::FILE);

        bite_im::FileMessageInfo fileMessageInfo;
        fileMessageInfo.setFileId("");
        fileMessageInfo.setFileSize(content.size());
        fileMessageInfo.setFileName(extraInfo);
        fileMessageInfo.setFileContents(content);
        messageContent.setFileMessage(fileMessageInfo);
    }
    else if (messageType == MessageType::SPEECH_TYPE)
    {
        messageContent.setMessageType(bite_im::MessageTypeGadget::MessageType::SPEECH);

        bite_im::SpeechMessageInfo speechMessageInfo;
        speechMessageInfo.setFileId("");
        speechMessageInfo.setFileContents(content);
        messageContent.setSpeechMessage(speechMessageInfo);
    }
    else
        LOG("错误的消息类型! MessageType = ") << messageType;
    req.setMessage(messageContent);

    // 序列化
    QByteArray body = req.serialize(&serializer);
    LOG("[发送消息] 发送请求 requestId = ") << req.requestId()
                                        << ", chatSessionId = " << chatSessionId
                                        << ", messageType = " << req.message().messageType();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/message_transmit/new_message", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto sendMegResp = this->handleHttpResponse<bite_im::NewMessageRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[发送消息] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        // dataCenter->resetRecentMessageList(chatSessionId, sendMegResp);

        // d> 通知
        emit dataCenter->sendMessageDone(messageType, content, extraInfo);

        // e> 打印日志
        LOG("[发送消息] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::receiveMessage(const QString &chatSessionId)
{
    // 判定收到消息对应的会话 是否是正在被用户选中的 "当前会话"
    // 是当前会话 就需要把消息 显示到消息展示区 也需要更新会话列表的消息预览
    // 不是当前会话 只需要更新会话列表中的消息预览即可 并且更新 "未读消息数目"
    if (chatSessionId == dataCenter->getCurrentChatSessionId())
    {
        // 收到的消息会话 就是选中会话
        // 在消息展示区 新增一个消息
        const Message& lastMessage = dataCenter->getRecentMessageList(chatSessionId)->back();
        // 通过信号, 让 NetClient 模块, 能够通知界面(消息展示区)
        emit dataCenter->receiveMessageDone(lastMessage);
    }
    else
    {
        // 收到的消息会话 不是选中会话
        // 更新未读消息数目
        dataCenter->addUnread(chatSessionId);
    }
    // 统一更新会话列表的消息预览
    emit dataCenter->updateLastMessage(chatSessionId);
}

void NetClient::changeNickname(const QString &loginSessionId, const QString &nickname)
{
    // 1. 通过 protobuf 构造 body
    bite_im::SetUserNicknameReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setNickname(nickname);
    QByteArray body = req.serialize(&serializer);
    LOG("[修改用户昵称] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << loginSessionId;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/set_nickname", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto nicknameMegResp = this->handleHttpResponse<bite_im::SetUserNicknameRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[修改用户昵称] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetNickname(nickname);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->changeNicknameDone();

        // e> 打印日志
        LOG("[修改用户昵称] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::changeDescription(const QString &loginSessionId, const QString &desc)
{
    // 1. 通过 protobuf 构造 body
    bite_im::SetUserDescriptionReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setDescription(desc);
    QByteArray body = req.serialize(&serializer);
    LOG("[修改个性签名] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << loginSessionId
                                                << ", desc = " << desc;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/set_description", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto nicknameMegResp = this->handleHttpResponse<bite_im::SetUserDescriptionRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[修改个性签名] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetDescription(desc);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->changeDescriptionDone();

        // e> 打印日志
        LOG("[修改个性签名] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::getVerifyCode(const QString &phone)
{
    // 1. 通过 protobuf 构造 body
    bite_im::PhoneVerifyCodeReq req;
    req.setRequestId(makeRequestId());
    req.setPhoneNumber(phone);
    QByteArray body = req.serialize(&serializer);
    LOG("[获取手机验证码] 发送请求 requestId = ") << req.requestId()
                                                << ", phone = " << phone;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/get_phone_verify_code", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto getVerifyCodeResp = this->handleHttpResponse<bite_im::PhoneVerifyCodeRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[获取手机验证码] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetVerifyCodeId(getVerifyCodeResp->verifyCodeId());

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->getVerifyCodeDone();

        // e> 打印日志
        LOG("[获取手机验证码] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::changePhone(const QString &loginSessionId, const QString &phone, const QString &verifyCodeId, const QString &verifyCode)
{
    // 1. 通过 protobuf 构造 body
    bite_im::SetUserPhoneNumberReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setPhoneNumber(phone);
    req.setPhoneVerifyCode(verifyCode);
    req.setPhoneVerifyCodeId(verifyCodeId);
    QByteArray body = req.serialize(&serializer);
    LOG("[修改手机号] 发送请求 requestId = ") << req.requestId()
                                              << ", loginSessionId = " << req.sessionId()
                                            << ", phone = " << req.phoneNumber()
                                              << ", verifyCodeId = " << req.phoneVerifyCodeId()
                                              << ", verifyCode = " << req.phoneVerifyCode();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/set_phone", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto setPhoneResp = this->handleHttpResponse<bite_im::SetUserPhoneNumberRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[修改手机号] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetPhone(phone);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->changePhoneDone();

        // e> 打印日志
        LOG("[修改手机号] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::changeAvatar(const QString &loginSessionId, const QByteArray &avatar)
{
    // 1. 通过 protobuf 构造 body
    bite_im::SetUserAvatarReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setAvatar(avatar);
    QByteArray body = req.serialize(&serializer);
    LOG("[修改头像] 发送请求 requestId = ") << req.requestId()
                                              << ", loginSessionId = " << req.sessionId();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/set_avatar", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto changeAvaResp = this->handleHttpResponse<bite_im::SetUserAvatarRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[修改头像] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->resetAvatar(avatar);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->changeAvatarDone();

        // e> 打印日志
        LOG("[修改头像] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::deleteFriend(const QString &loginSessionId, const QString &userId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::FriendRemoveReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setPeerId(userId);
    QByteArray body = req.serialize(&serializer);
    LOG("[删除好友] 发送请求 requestId = ") << req.requestId()
                                            << ", loginSessionId = " << req.sessionId()
                                            << ", peerId = " << req.peerId();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/remove_friend", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto delFriResp = this->handleHttpResponse<bite_im::FriendRemoveRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[删除好友] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存在 DataCenter 中
        dataCenter->removeFriend(userId);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->deleteFriendDone();

        // e> 打印日志
        LOG("[删除好友] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::addFriendApply(const QString &loginSessionId, const QString &userId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::FriendAddReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setRequestId(userId);
    QByteArray body = req.serialize(&serializer);
    LOG("[添加好友申请] 发送请求 requestId = ") << req.requestId()
                                            << ", loginSessionId = " << req.sessionId()
                                            << ", userId = " << userId;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/add_friend_apply", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto addFriResp = this->handleHttpResponse<bite_im::FriendAddRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[添加好友申请] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 把结果保存

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->addFriendApplyDone();

        // e> 打印日志
        LOG("[添加好友申请] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::acceptFriendApply(const QString &loginSessionId, const QString &userId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::FriendAddProcessReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setApplyUserId(userId);
    req.setAgree(true);
    QByteArray body = req.serialize(&serializer);
    LOG("[同意好友申请] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << req.sessionId()
                                                << ", userId = " << req.applyUserId();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/add_friend_process", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto addFriResp = this->handleHttpResponse<bite_im::FriendAddProcessRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[同意好友申请] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 好友列表更新
        UserInfo applyUser = dataCenter->removeFromApplyList(userId);
        QList<UserInfo>* friendList = dataCenter->getFriendList();
        friendList->push_front(applyUser);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->acceptFriendApplyDone();

        // e> 打印日志
        LOG("[同意好友申请] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::rejectFriendApply(const QString &loginSessionId, const QString &userId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::FriendAddProcessReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setApplyUserId(userId);
    req.setAgree(false);
    QByteArray body = req.serialize(&serializer);
    LOG("[拒绝好友申请] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << req.sessionId()
                                                << ", userId = " << req.applyUserId();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/add_friend_process", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto addFriResp = this->handleHttpResponse<bite_im::FriendAddProcessRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[拒绝好友申请] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 好友列表更新
        dataCenter->removeFromApplyList(userId);

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->rejectFriendApplyDone();

        // e> 打印日志
        LOG("[拒绝好友申请] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::createGroupChatSession(const QString &loginSessionId, const QList<QString> &userIdList)
{
    // 1. 通过 protobuf 构造 body
    bite_im::ChatSessionCreateReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionName("新的群聊");
    req.setMemberIdList(userIdList);
    QByteArray body = req.serialize(&serializer);
    LOG("[创建群聊会话] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << req.sessionId()
                                                << ", userIdList = " << userIdList;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/create_chat_session", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::ChatSessionCreateRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[创建群聊会话] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 通过websocket来更新

        // d> 发送信号 通知界面 操作完成
        emit dataCenter->createGroupChatSessionDone();

        // e> 打印日志
        LOG("[创建群聊会话] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::getMemberList(const QString &loginSessionId, const QString &chatSessionId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::GetChatSessionMemberReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);
    QByteArray body = req.serialize(&serializer);
    LOG("[获取成员列表] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << req.sessionId()
                                                << ", chatSessionId = " << req.chatSessionId();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/get_chat_session_member", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::GetChatSessionMemberRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[获取成员列表] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 通过 dataCenter 记录结果
        dataCenter->resetMemberList(chatSessionId, pbResp->memberInfoList());

        // d> 发送信号 通知界面 操作完成

        emit dataCenter->getMemberListDone(chatSessionId);

        // e> 打印日志
        LOG("[获取成员列表] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::searchUser(const QString &loginSessionId, const QString &searchKey)
{
    // 1. 通过 protobuf 构造 body
    bite_im::FriendSearchReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setSearchKey(searchKey);
    QByteArray body = req.serialize(&serializer);
    LOG("[搜索用户] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << req.sessionId()
                                                << ", searchKey = " << req.searchKey();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/friend/search_friend", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::FriendSearchRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[搜索用户] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 通过 dataCenter 记录结果
        dataCenter->resetSearchUserResult(pbResp->userInfo());

        // d> 发送信号 通知界面 操作完成

        emit dataCenter->searchUserDone();

        // e> 打印日志
        LOG("[搜索用户] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::searchMessage(const QString &loginSessionId, const QString &chatSessionId, const QString &searchKey)
{
    // 1. 通过 protobuf 构造 body
    bite_im::MsgSearchReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);
    req.setSearchKey(searchKey);
    QByteArray body = req.serialize(&serializer);
    LOG("[按关键词搜索历史消息] 发送请求 requestId = ") << req.requestId()
                                            << ", loginSessionId = " << req.sessionId()
                                            << ", chatSessionId = " << req.chatSessionId()
                                            << ", searchKey = " << req.searchKey();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/message_storage/search_history", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::MsgSearchRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[按关键词搜索历史消息] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 通过 dataCenter 记录结果
        dataCenter->resetSearchMsgResult(pbResp->msgList());

        // d> 发送信号 通知界面 操作完成

        emit dataCenter->searchMsgDone();

        // e> 打印日志
        LOG("[按关键词搜索历史消息] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::searchMsgByTime(const QString &loginSessionId, const QString &chatSessionId, const QDateTime &begTime, const QDateTime &endTime)
{
    // 1. 通过 protobuf 构造 body
    bite_im::GetHistoryMsgReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setChatSessionId(chatSessionId);
    req.setStartTime(begTime.toSecsSinceEpoch());
    req.setOverTime(endTime.toSecsSinceEpoch());
    QByteArray body = req.serialize(&serializer);
    LOG("[按时间搜索历史消息] 发送请求 requestId = ") << req.requestId()
        << ", loginSessionId = " << req.sessionId() << ", chatSessionId = " << req.chatSessionId()
        << ", begTime = " << begTime << ", endTime = " << endTime;

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/message_storage/get_history", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::GetHistoryMsgRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[按时间搜索历史消息] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }

        // c> 通过 dataCenter 记录结果
        dataCenter->resetSearchMsgResult(pbResp->msgList());

        // d> 发送信号 通知界面 操作完成

        emit dataCenter->searchMsgDone();

        // e> 打印日志
        LOG("[按时间搜索历史消息] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::userLogin(const QString &username, const QString &password)
{
    // 1. 通过 protobuf 构造 body
    bite_im::UserLoginReq req;
    req.setRequestId(makeRequestId());
    req.setNickname(username);
    req.setPassword(password);
    req.setVerifyCodeId("");
    req.setVerifyCode("");
    QByteArray body = req.serialize(&serializer);
    LOG("[用户名登录] 发送请求 requestId = ") << req.requestId()
                                            << ", username = " << req.nickname()
                                            << ", password = " << req.password();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/username_login", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::UserLoginRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[用户名登录] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            emit dataCenter->userLoginDone(false, reason);
            return;
        }

        // c> 通过 dataCenter 记录结果
        dataCenter->resetLoginSessionId(pbResp->loginSessionId());

        // d> 发送信号 通知界面 操作完成

        emit dataCenter->userLoginDone(true, "");

        // e> 打印日志
        LOG("[用户名登录] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::useRegister(const QString &username, const QString &password)
{
    // 1. 通过 protobuf 构造 body
    bite_im::UserRegisterReq req;
    req.setRequestId(makeRequestId());
    req.setNickname(username);
    req.setPassword(password);
    req.setVerifyCodeId("");
    req.setVerifyCode("");
    QByteArray body = req.serialize(&serializer);
    LOG("[用户名注册] 发送请求 requestId = ") << req.requestId()
                                              << ", username = " << req.nickname()
                                              << ", password = " << req.password();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/username_register", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::UserRegisterRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[用户名注册] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            emit dataCenter->userRegisterDone(false, reason);
            return;
        }
        // c> 注册无需记录保存


        // d> 发送信号 通知界面 操作完成

        emit dataCenter->userRegisterDone(true, "");

        // e> 打印日志
        LOG("[用户名注册] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::phoneLogin(const QString &phone, const QString &verifyCodeId, const QString &verifyCode)
{
    // 1. 通过 protobuf 构造 body
    bite_im::PhoneLoginReq req;
    req.setRequestId(makeRequestId());
    req.setPhoneNumber(phone);
    req.setVerifyCodeId(verifyCodeId);
    req.setVerifyCode(verifyCode);
    QByteArray body = req.serialize(&serializer);
    LOG("[手机号登录] 发送请求 requestId = ") << req.requestId()
                                              << ", phone = " << req.phoneNumber()
                                              << ", verifyCodeId = " << req.verifyCodeId()
                                              << ", verifyCode = " << req.verifyCode();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/phone_login", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::PhoneLoginRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[手机号登录] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            emit dataCenter->phoneLoginDone(false, reason);
            return;
        }
        // c> 响应结果记录保存
        dataCenter->resetLoginSessionId(pbResp->loginSessionId());


        // d> 发送信号 通知界面 操作完成

        emit dataCenter->phoneLoginDone(true, "");

        // e> 打印日志
        LOG("[手机号登录] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::phoneRegister(const QString &phone, const QString &verifyCodeId, const QString &verifyCode)
{
    // 1. 通过 protobuf 构造 body
    bite_im::PhoneRegisterReq req;
    req.setRequestId(makeRequestId());
    req.setPhoneNumber(phone);
    req.setVerifyCodeId(verifyCodeId);
    req.setVerifyCode(verifyCode);
    QByteArray body = req.serialize(&serializer);
    LOG("[手机号注册] 发送请求 requestId = ") << req.requestId()
                                              << ", phone = " << req.phoneNumber()
                                              << ", verifyCodeId = " << req.verifyCodeId()
                                              << ", verifyCode = " << req.verifyCode();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/user/phone_register", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::PhoneRegisterRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[手机号注册] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            emit dataCenter->phoneRegisterDone(false, reason);
            return;
        }
        // c> 响应结果记录保存


        // d> 发送信号 通知界面 操作完成

        emit dataCenter->phoneRegisterDone(true, "");

        // e> 打印日志
        LOG("[手机号注册] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::getSingleFile(const QString &loginSessionId, const QString &fileId)
{
    // 1. 通过 protobuf 构造 body
    bite_im::GetSingleFileReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setFileId(fileId);
    QByteArray body = req.serialize(&serializer);
    LOG("[获取文件内容] 发送请求 requestId = ") << req.requestId()
                                              << ", loginSessionId = " << req.sessionId()
                                              << ", fileId = " << req.fileId();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/file/get_single_file", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::GetSingleFileRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[获取文件内容] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            emit dataCenter->phoneRegisterDone(false, reason);
            return;
        }
        // c> 响应结果记录保存


        // d> 发送信号 通知界面 操作完成

        emit dataCenter->getSingleFileDone(fileId, pbResp->fileData().fileContent());

        // e> 打印日志
        LOG("[获取文件内容] 处理响应完毕! requestId = ") << req.requestId();
    });
}

void NetClient::speechConvertText(const QString &loginSessionId, const QString &fileId, const QByteArray &content)
{
    // 1. 通过 protobuf 构造 body
    bite_im::SpeechRecognitionReq req;
    req.setRequestId(makeRequestId());
    req.setSessionId(loginSessionId);
    req.setSpeechContent(content);
    QByteArray body = req.serialize(&serializer);
    LOG("[语音转文字] 发送请求 requestId = ") << req.requestId()
                                                << ", loginSessionId = " << req.sessionId();

    // 2. 发送 HTTP 请求
    QNetworkReply* httpResp = this->sendHttpRequest("/service/speech/recognition", body);

    // 3. 处理响应
    connect(httpResp, &QNetworkReply::finished, this, [=](){
        // a> 解析响应
        bool ok = false;
        QString reason;
        auto pbResp = this->handleHttpResponse<bite_im::SpeechRecognitionRsp>(httpResp, &ok, &reason);

        // b> 判定响应是否正确
        if (!ok)
        {
            LOG("[语音转文字] 失败! requestId = ") << req.requestId() << ", reason = " << reason;
            return;
        }
        // c> 响应结果记录保存


        // d> 发送信号 通知界面 操作完成

        emit dataCenter->speechConvertTextDone(fileId, pbResp->recognitionResult());

        // e> 打印日志
        LOG("[语音转文字] 处理响应完毕! requestId = ") << req.requestId();
    });
}





}
